#!/usr/bin/evn python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   文件名称 :     serveis
   文件功能描述 :   功能描述
   创建人 :       小钟同学
-------------------------------------------------
   修改描述- 用于多继承，实现相关Serveis服务辅助功能的扩展
   PS：这个主要是早期的使用用于扩展注册服务的一些额外的功能的独立，后面引入的全局代理之后这个可以看着使用了！
   PS: 这个其实和下面使用全局代理实现类似，这里其实有点重复了！不过看场景来用，或可以去除
-------------------------------------------------
"""

# 引入到这个的会报错# NamedTuple
from google.protobuf import json_format
from pydantic import ValidationError
from grpcframe.exception import BadRequestException
from grpcframe.validation import raw_errors_info
import inspect
from typing import Dict, Tuple


class RequestContext(object):
    def __init__(self, request, context=None):
        self.request = request
        self.context = context

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


class ComposeParseRequestContext:
    pass
    '''
    基类的主要作用：用于多继承，实现相关Serveis服务辅助功能的扩展
    1：设置头信息
    2：解析请求参数信息
    3：对参数信息的校验
    4：对请求头信息的解析转为字典
    '''
    # 用户判断多继承的时候，需要需要传递app实例引用的依据
    composeapp_type: bool = True

    def __init__(self, *args, **kwargs):
        pass
        # print("每次的只实例化一次")
        self._metadata = None
        self._request = None
        self.app = None
        # 服务注册的时候，传递当前的APP实例对象
        for item in args:
            is_init_app_complete = False
            for _, _servicer in inspect.getmembers(item, inspect.isclass):
                if _servicer.__name__ == "FastGrpcApp":
                    # app实例对象
                    from grpcframe.zyxgrpc.base_app import FastGrpcApp
                    is_init_app_complete = True
                    self.app: FastGrpcApp = item
                    break
            if is_init_app_complete:
                break

    def make_request_context(self, request, context):
        # 返回当前的上下文信息对象
        self.request_context: RequestContext = RequestContext(request=request, context=context)
        return self.request_context

    @property
    def name(self):
        return self.__class__.__name__

    def rpc_event(self, context):
        """ 返回RPC事件
        """
        return getattr(context, "_rpc_event")

    def call_details(self, context):
        """
        从rpc_event获取我们的---调用详情信息
        从调用详情里面可以获取信息比较多：如方法，还有方法名
        """
        if self.rpc_event(context) is not None:
            return getattr(self.rpc_event(context), "call_details")
        return None

    def method(self, context):
        """  从rpc_event获取我们的---调用详情信息--从调用详情信息里面：
             获取本次请求调用的方法 : /Greeter/SayHelloAgain
        """
        if self.call_details(context) is not None:
            method = getattr(self.call_details(context), "method")
            return method.decode("utf8") if method else method
        return None

    def service(self, context):
        """  从rpc_event获取我们的---调用详情信息--从调用详情信息里面：
             获取本次请求调用的方法 ---从调用方法里面截取到--本次请求的Service
             这里和我们的
          @property
          def name(self):
             return self.__class__.__name__ 是一样的就是获取服务的类名
        """
        if self.method(context) is not None:
            return self.method(context).split("/")[-2]
        return None

    def set_trailing_metadata(self, context):
        '''
        通过设置字典的形式设置我们的响应头信息,只能调用一次的设置，不然会引发服务的异常
        :param context:
        :return:
        context.set_trailing_metadata((('name', '2232232'), ('sex', '23232')))
        '''
        pass

    def parse_metadata(self) -> Dict:
        '''
        获取请求头信息
        :param context:
        :return:
        '''
        metadata = {}
        context = self.request_context.context
        for key, value in context.invocation_metadata():
            metadata[key.strip()] = value.strip()
        # 返回信息
        metadata.update({'peer': context.peer()})
        return metadata

    def parse_request(self) -> Dict:
        '''
        获取当前RPC的请求参数信息，进行字典的序列化
        :param request:
        :return:
        '''
        request = self.request_context.request
        request_dict = json_format.MessageToDict(request, preserving_proto_field_name=True)
        return request_dict

    def parse_request_action(self, request) -> Dict:
        '''
        获取当前RPC的请求参数信息，进行字典的序列化
        :param request:
        :return:
        '''
        request_dict = json_format.MessageToDict(request, preserving_proto_field_name=True)
        return request_dict

    def validation_request(self, model: type):
        request_model = None
        try:
            # 模型实例化
            request_model = model(**self.parse_request())
            pass
        except ValidationError as exc:
            raise BadRequestException(message=raw_errors_info(exc))
        finally:
            return request_model

    def parse_request_metadata(self) -> Tuple:
        '''
        解析请求参数和请求头信息并返回
        :param request:
        :param context:
        :return:
        '''
        return self.parse_request(), self.parse_metadata()
